package com.linkedin.thirdeye.impl;

import com.linkedin.thirdeye.api.MetricType;

import java.io.DataOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;

/**
 * Helper class to convert to and from Number data type. - supports arithmetic
 * on two number data types
 *
 * @author kgopalak
 *
 */
public class NumberUtils {

  public static Number sum(Number a, Number b, MetricType type) {
    switch (type) {
      case SHORT: return a.shortValue() + b.shortValue();
      case INT: return a.intValue() + b.intValue();
      case LONG: return a.longValue() + b.longValue();
      case FLOAT: return a.floatValue() + b.floatValue();
      case DOUBLE: return a.doubleValue() + b.doubleValue();
      default: return null;
    }
  }

  public static Number difference(Number a, Number b, MetricType type) {
    switch (type) {
      case SHORT: return a.shortValue() - b.shortValue();
      case INT: return a.intValue() - b.intValue();
      case LONG: return a.longValue() - b.longValue();
      case FLOAT: return a.floatValue() - b.floatValue();
      case DOUBLE: return a.doubleValue() - b.doubleValue();
      default: return null;
    }
  }

  public static void addToBuffer(ByteBuffer buffer, Number value, MetricType type) {
    switch (type) {
      case SHORT: buffer.putShort(value.shortValue()); break;
      case INT: buffer.putInt(value.intValue()); break;
      case LONG: buffer.putLong(value.longValue()); break;
      case FLOAT: buffer.putFloat(value.floatValue()); break;
      case DOUBLE: buffer.putDouble(value.doubleValue()); break;
    }
  }

  public static Number readFromBuffer(ByteBuffer buffer, MetricType type) {
    switch (type) {
      case SHORT: return buffer.getShort();
      case INT: return buffer.getInt();
      case LONG: return buffer.getLong();
      case FLOAT: return buffer.getFloat();
      case DOUBLE: return buffer.getDouble();
    }
    return null;
  }

  public static void addToDataOutputStream(DataOutputStream dataOutputStream,
      Number value, MetricType type) throws IOException {
    switch (type) {
      case SHORT: dataOutputStream.writeShort(value.shortValue()); break;
      case INT: dataOutputStream.writeInt(value.intValue()); break;
      case LONG: dataOutputStream.writeLong(value.longValue()); break;
      case FLOAT: dataOutputStream.writeFloat(value.floatValue()); break;
      case DOUBLE: dataOutputStream.writeDouble(value.doubleValue()); break;
    }
  }

  // TODO: Remove
  public static int byteSize(MetricType type) {
    return type.byteSize();
  }

  public static Number divide(Number numerator, Number denominator, MetricType type)
  {
    switch (type)
    {
      case SHORT:
        return numerator.shortValue() / denominator.shortValue();
      case INT:
        return numerator.intValue() / denominator.intValue();
      case LONG:
        return numerator.longValue() / denominator.longValue();
      case FLOAT:
        return numerator.floatValue() / denominator.floatValue();
      case DOUBLE:
        return numerator.doubleValue() / denominator.doubleValue();
    }
    return -1;
  }

  public static boolean isZero(Number value, MetricType type)
  {
    switch (type)
    {
      case SHORT:
        return value.shortValue() == 0;
      case INT:
        return value.intValue() == 0;
      case LONG:
        return value.longValue() == 0L;
      case FLOAT:
        return value.floatValue() == 0.0;
      case DOUBLE:
        return value.doubleValue() == 0.0;
    }
    throw new IllegalArgumentException("Invalid type " + type);
  }

  public static Number valueOf(String value, MetricType type)
  {
    switch (type)
    {
      case SHORT:
        return Short.valueOf(value);
      case INT:
        return Integer.valueOf(value);
      case LONG:
        return Long.valueOf(value);
      case FLOAT:
        return Float.valueOf(value);
      case DOUBLE:
        return Double.valueOf(value);
    }
    throw new IllegalArgumentException("Invalid type " + type);
  }
}
